pomera-ai-commander 1.2.8 → 1.2.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,129 +1,153 @@
1
- # Pomera AI Commander (PAC)
1
+ # MCP Registry
2
2
 
3
- <p align="center">
4
- <img src="resources/icon.png" alt="Pomera - the fluffy Pomeranian mascot" width="128" height="128">
5
- </p>
3
+ The MCP registry provides MCP clients with a list of MCP servers, like an app store for MCP servers.
6
4
 
7
- [![Download Latest Release](https://img.shields.io/badge/Download-Latest%20Release-blue?style=for-the-badge&logo=github)](https://github.com/matbanik/Pomera-AI-Commander/releases)
5
+ [**📤 Publish my MCP server**](docs/modelcontextprotocol-io/quickstart.mdx) | [**⚡️ Live API docs**](https://registry.modelcontextprotocol.io/docs) | [**👀 Ecosystem vision**](docs/design/ecosystem-vision.md) | 📖 **[Full documentation](./docs)**
8
6
 
9
- A desktop text "workbench" + MCP server: clean, transform, extract, and analyze text fast—manually in a GUI or programmatically from AI assistants (Cursor / Claude Desktop / MCP clients).
7
+ ## Development Status
10
8
 
11
- > Hook: Stop pasting text into 10 random websites. Pomera gives you one place (GUI + MCP) to do the 90% text ops you repeat every week.
9
+ **2025-10-24 update**: The Registry API has entered an **API freeze (v0.1)** 🎉. For the next month or more, the API will remain stable with no breaking changes, allowing integrators to confidently implement support. This freeze applies to v0.1 while development continues on v0. We'll use this period to validate the API in real-world integrations and gather feedback to shape v1 for general availability. Thank you to everyone for your contributions and patience—your involvement has been key to getting us here!
12
10
 
13
- [Download latest release](https://github.com/matbanik/Pomera-AI-Commander/releases) · Docs: [Tools](docs/TOOLS_DOCUMENTATION.md) · [MCP Guide](docs/MCP_SERVER_GUIDE.md) · [Troubleshooting](docs/TROUBLESHOOTING.md)
11
+ **2025-09-08 update**: The registry has launched in preview 🎉 ([announcement blog post](https://blog.modelcontextprotocol.io/posts/2025-09-08-mcp-registry-preview/)). While the system is now more stable, this is still a preview release and breaking changes or data resets may occur. A general availability (GA) release will follow later. We'd love your feedback in [GitHub discussions](https://github.com/modelcontextprotocol/registry/discussions/new?category=ideas) or in the [#registry-dev Discord](https://discord.com/channels/1358869848138059966/1369487942862504016) ([joining details here](https://modelcontextprotocol.io/community/communication)).
14
12
 
15
- ---
13
+ Current key maintainers:
14
+ - **Adam Jones** (Anthropic) [@domdomegg](https://github.com/domdomegg)
15
+ - **Tadas Antanavicius** (PulseMCP) [@tadasant](https://github.com/tadasant)
16
+ - **Toby Padilla** (GitHub) [@toby](https://github.com/toby)
17
+ - **Radoslav (Rado) Dimitrov** (Stacklok) [@rdimitrov](https://github.com/rdimitrov)
16
18
 
17
- ## 60-second demo (what to expect)
18
- ![Messy text → clean output → extracted URLs/emails → ready to ship](PAC.gif)
19
+ ## Contributing
19
20
 
20
- **Best-for workflows**
21
- - Cleaning pasted logs / PDFs (whitespace, wrapping, stats)
22
- - Extracting emails/URLs/IDs via regex
23
- - Normalizing case, sorting, columns
24
- - Hashing/encoding utilities
25
- - Letting Cursor/Claude call these as MCP tools in a repeatable pipeline
21
+ We use multiple channels for collaboration - see [modelcontextprotocol.io/community/communication](https://modelcontextprotocol.io/community/communication).
26
22
 
27
- ---
23
+ Often (but not always) ideas flow through this pipeline:
28
24
 
29
- ## Prerequisites
25
+ - **[Discord](https://modelcontextprotocol.io/community/communication)** - Real-time community discussions
26
+ - **[Discussions](https://github.com/modelcontextprotocol/registry/discussions)** - Propose and discuss product/technical requirements
27
+ - **[Issues](https://github.com/modelcontextprotocol/registry/issues)** - Track well-scoped technical work
28
+ - **[Pull Requests](https://github.com/modelcontextprotocol/registry/pulls)** - Contribute work towards issues
30
29
 
31
- **Python 3.8+** is required for all installation methods.
30
+ ### Quick start:
32
31
 
33
- ### macOS (Homebrew)
34
- ```bash
35
- # Tkinter support (replace @3.14 with your Python version)
36
- brew install python-tk@3.14
37
- pip3 install requests reportlab python-docx
38
- ```
32
+ #### Pre-requisites
33
+
34
+ - **Docker**
35
+ - **Go 1.24.x**
36
+ - **ko** - Container image builder for Go ([installation instructions](https://ko.build/install/))
37
+ - **golangci-lint v2.4.0**
38
+
39
+ #### Running the server
39
40
 
40
- ### Ubuntu/Debian
41
41
  ```bash
42
- sudo apt-get install python3-tk
43
- pip3 install requests reportlab python-docx
42
+ # Start full development environment
43
+ make dev-compose
44
44
  ```
45
45
 
46
- ### Windows
47
- Tkinter is included with Python from [python.org](https://python.org).
48
- ```cmd
49
- pip install requests reportlab python-docx
50
- ```
46
+ This starts the registry at [`localhost:8080`](http://localhost:8080) with PostgreSQL. The database uses ephemeral storage and is reset each time you restart the containers, ensuring a clean state for development and testing.
47
+
48
+ **Note:** The registry uses [ko](https://ko.build) to build container images. The `make dev-compose` command automatically builds the registry image with ko and loads it into your local Docker daemon before starting the services.
51
49
 
52
- > **Note:** For PEP 668 protected environments, use `pip3 install --user` or a virtual environment.
50
+ By default, the registry seeds from the production API with a filtered subset of servers (to keep startup fast). This ensures your local environment mirrors production behavior and all seed data passes validation. For offline development you can seed from a file without validation with `MCP_REGISTRY_SEED_FROM=data/seed.json MCP_REGISTRY_ENABLE_REGISTRY_VALIDATION=false make dev-compose`.
53
51
 
54
- ---
52
+ The setup can be configured with environment variables in [docker-compose.yml](./docker-compose.yml) - see [.env.example](./.env.example) for a reference.
55
53
 
56
- ## Install / Run
57
- ### Option A Prebuilt executable (recommended)
58
- [Download from Releases](https://github.com/matbanik/Pomera-AI-Commander/releases) and run.
54
+ <details>
55
+ <summary>Alternative: Running a pre-built Docker image</summary>
56
+
57
+ Pre-built Docker images are automatically published to GitHub Container Registry:
59
58
 
60
- ### Option B — Python (PyPI)
61
59
  ```bash
62
- pip install pomera-ai-commander
63
- # then run:
64
- pomera-ai-commander --help
60
+ # Run latest stable release
61
+ docker run -p 8080:8080 ghcr.io/modelcontextprotocol/registry:latest
62
+
63
+ # Run latest from main branch (continuous deployment)
64
+ docker run -p 8080:8080 ghcr.io/modelcontextprotocol/registry:main
65
+
66
+ # Run specific release version
67
+ docker run -p 8080:8080 ghcr.io/modelcontextprotocol/registry:v1.0.0
68
+
69
+ # Run development build from main branch
70
+ docker run -p 8080:8080 ghcr.io/modelcontextprotocol/registry:main-20250906-abc123d
65
71
  ```
66
72
 
67
- ### Option C — Node.js (npm)
73
+ **Available tags:**
74
+ - **Releases**: `latest`, `v1.0.0`, `v1.1.0`, etc.
75
+ - **Continuous**: `main` (latest main branch build)
76
+ - **Development**: `main-<date>-<sha>` (specific commit builds)
77
+
78
+ </details>
79
+
80
+ #### Publishing a server
81
+
82
+ To publish a server, we've built a simple CLI. You can use it with:
83
+
68
84
  ```bash
69
- npm install -g pomera-ai-commander
70
- # then run:
71
- pomera-mcp --help
85
+ # Build the latest CLI
86
+ make publisher
87
+
88
+ # Use it!
89
+ ./bin/mcp-publisher --help
72
90
  ```
73
91
 
74
- ### Create Desktop Shortcut
75
- After installing via pip or npm, create a desktop shortcut for quick access:
92
+ See [the publisher guide](./docs/modelcontextprotocol-io/quickstart.mdx) for more details.
76
93
 
77
- ```bash
78
- # For pip install:
79
- pomera-create-shortcut
94
+ #### Other commands
80
95
 
81
- # For npm install (from package directory):
82
- python create_shortcut.py
96
+ ```bash
97
+ # Run lint, unit tests and integration tests
98
+ make check
83
99
  ```
84
100
 
85
- ---
101
+ There are also a few more helpful commands for development. Run `make help` to learn more, or look in [Makefile](./Makefile).
86
102
 
87
- ## MCP Server for AI Assistants
103
+ <!--
104
+ For Claude and other AI tools: Always prefer make targets over custom commands where possible.
105
+ -->
88
106
 
89
- Pomera exposes 22 text processing tools via MCP. Configure your AI assistant:
107
+ ## Architecture
90
108
 
91
- **Cursor** (`.cursor/mcp.json`):
92
- ```json
93
- {
94
- "mcpServers": {
95
- "pomera": {
96
- "command": "pomera-ai-commander"
97
- }
98
- }
99
- }
100
- ```
109
+ ### Project Structure
101
110
 
102
- **Claude Desktop** (`claude_desktop_config.json`):
103
- ```json
104
- {
105
- "mcpServers": {
106
- "pomera": {
107
- "command": "pomera-ai-commander"
108
- }
109
- }
110
- }
111
111
  ```
112
+ ├── cmd/ # Application entry points
113
+ │ └── publisher/ # Server publishing tool
114
+ ├── data/ # Seed data
115
+ ├── deploy/ # Deployment configuration (Pulumi)
116
+ ├── docs/ # Documentation
117
+ ├── internal/ # Private application code
118
+ │ ├── api/ # HTTP handlers and routing
119
+ │ ├── auth/ # Authentication (GitHub OAuth, JWT, namespace blocking)
120
+ │ ├── config/ # Configuration management
121
+ │ ├── database/ # Data persistence (PostgreSQL)
122
+ │ ├── service/ # Business logic
123
+ │ ├── telemetry/ # Metrics and monitoring
124
+ │ └── validators/ # Input validation
125
+ ├── pkg/ # Public packages
126
+ │ ├── api/ # API types and structures
127
+ │ │ └── v0/ # Version 0 API types
128
+ │ └── model/ # Data models for server.json
129
+ ├── scripts/ # Development and testing scripts
130
+ ├── tests/ # Integration tests
131
+ └── tools/ # CLI tools and utilities
132
+ └── validate-*.sh # Schema validation tools
133
+ ```
134
+
135
+ ### Authentication
136
+
137
+ Publishing supports multiple authentication methods:
138
+ - **GitHub OAuth** - For publishing by logging into GitHub
139
+ - **GitHub OIDC** - For publishing from GitHub Actions
140
+ - **DNS verification** - For proving ownership of a domain and its subdomains
141
+ - **HTTP verification** - For proving ownership of a domain
112
142
 
113
- > **💡 Tip:** If the simple command doesn't work, use the full path. Find it with:
114
- > ```bash
115
- > # For npm install:
116
- > npm root -g
117
- > # Then use: <result>/pomera-ai-commander/pomera_mcp_server.py
118
- >
119
- > # For pip install:
120
- > pip show pomera-ai-commander | grep Location
121
- > ```
143
+ The registry validates namespace ownership when publishing. E.g. to publish...:
144
+ - `io.github.domdomegg/my-cool-mcp` you must login to GitHub as `domdomegg`, or be in a GitHub Action on domdomegg's repos
145
+ - `me.adamjones/my-cool-mcp` you must prove ownership of `adamjones.me` via DNS or HTTP challenge
122
146
 
123
- See the full [MCP Server Guide](docs/MCP_SERVER_GUIDE.md) for Antigravity, executable configs, and troubleshooting.
147
+ ## Community Projects
124
148
 
125
- ---
149
+ Check out [community projects](docs/community-projects.md) to explore notable registry-related work created by the community.
126
150
 
127
- ## License
151
+ ## More documentation
128
152
 
129
- MIT License - see [LICENSE](LICENSE) for details.
153
+ See the [documentation](./docs) for more details if your question has not been answered here!
@@ -398,6 +398,21 @@ class DataTypeConverter:
398
398
  """
399
399
  import json
400
400
 
401
+ # Handle None or empty string for all types
402
+ if value_str is None or value_str == '':
403
+ if data_type in ('json', 'array'):
404
+ return [] if data_type == 'array' else {}
405
+ elif data_type == 'str':
406
+ return ''
407
+ elif data_type == 'int':
408
+ return 0
409
+ elif data_type == 'float':
410
+ return 0.0
411
+ elif data_type == 'bool':
412
+ return False
413
+ else:
414
+ return ''
415
+
401
416
  if data_type == 'str':
402
417
  return value_str
403
418
  elif data_type == 'int':
@@ -407,6 +422,14 @@ class DataTypeConverter:
407
422
  elif data_type == 'bool':
408
423
  return value_str == '1'
409
424
  elif data_type in ('json', 'array'):
410
- return json.loads(value_str)
425
+ # Handle whitespace-only strings as empty
426
+ if not value_str.strip():
427
+ return [] if data_type == 'array' else {}
428
+ try:
429
+ return json.loads(value_str)
430
+ except json.JSONDecodeError as e:
431
+ # Debug: print what value caused the error
432
+ print(f"DEBUG: JSON parse failed for data_type='{data_type}': value_str='{value_str[:100]}...' error={e}")
433
+ return [] if data_type == 'array' else {}
411
434
  else:
412
435
  return value_str # Fallback to string
@@ -308,8 +308,10 @@ class DatabaseSchemaManager:
308
308
  for index_sql in table_indexes:
309
309
  # Extract index name from CREATE INDEX statement
310
310
  parts = index_sql.split()
311
- if len(parts) >= 5 and parts[0].upper() == "CREATE" and parts[1].upper() == "INDEX":
312
- index_name = parts[4] # "CREATE INDEX IF NOT EXISTS index_name"
311
+ # "CREATE INDEX IF NOT EXISTS idx_name ON table(...)"
312
+ # parts: [0]=CREATE [1]=INDEX [2]=IF [3]=NOT [4]=EXISTS [5]=idx_name
313
+ if len(parts) >= 6 and parts[0].upper() == "CREATE" and parts[1].upper() == "INDEX":
314
+ index_name = parts[5] # After "CREATE INDEX IF NOT EXISTS"
313
315
  expected_indexes.add(index_name)
314
316
 
315
317
  # Get existing indexes
@@ -143,6 +143,27 @@ class NestedSettingsProxy:
143
143
  """Return a copy of the underlying data as a regular dictionary."""
144
144
  return self._data.copy()
145
145
 
146
+ def pop(self, key: str, *args):
147
+ """Remove and return a value from the nested settings.
148
+
149
+ Args:
150
+ key: Key to remove
151
+ *args: Optional default value if key not found
152
+
153
+ Returns:
154
+ The removed value, or default if provided and key not found
155
+ """
156
+ if args:
157
+ result = self._data.pop(key, args[0])
158
+ else:
159
+ result = self._data.pop(key)
160
+
161
+ # Save the change to database
162
+ full_path = f"{self._parent_key}"
163
+ self._settings_manager.set_setting(full_path, self._data)
164
+
165
+ return result
166
+
146
167
  def _update_nested_value(self, data: Dict[str, Any], path: str, value: Any) -> None:
147
168
  """Update value in nested dictionary using dot notation."""
148
169
  keys = path.split('.')
@@ -1112,8 +1133,10 @@ class DatabaseSettingsManager:
1112
1133
 
1113
1134
  critical_issues = [i for i in issues if i.severity == 'critical']
1114
1135
  if critical_issues:
1115
- self.logger.error(f"Imported settings have {len(critical_issues)} critical issues")
1116
- return False
1136
+ # Log specific issues for debugging but allow import to proceed
1137
+ for issue in critical_issues:
1138
+ self.logger.warning(f"Import validation issue: {issue.location} - {issue.message}")
1139
+ self.logger.warning(f"Imported settings have {len(critical_issues)} validation issues - proceeding anyway")
1117
1140
 
1118
1141
  # Save imported settings
1119
1142
  success = self.save_settings(settings_data)
@@ -283,8 +283,8 @@ class DialogManager:
283
283
  try:
284
284
  dialog_settings = self.settings_manager.get_setting("dialog_settings", {})
285
285
 
286
- # Validate settings structure
287
- if not isinstance(dialog_settings, dict):
286
+ # Validate settings structure - accept dict or dict-like objects (NestedSettingsProxy)
287
+ if not isinstance(dialog_settings, dict) and not hasattr(dialog_settings, 'get'):
288
288
  self.logger.warning(f"Invalid dialog_settings structure: {type(dialog_settings)}, using defaults")
289
289
  return True
290
290
 
@@ -663,8 +663,8 @@ class DialogManager:
663
663
  # Force a fresh read of dialog settings
664
664
  dialog_settings = self.settings_manager.get_setting("dialog_settings", {})
665
665
 
666
- # Validate settings structure
667
- if not isinstance(dialog_settings, dict):
666
+ # Validate settings structure - accept dict or dict-like objects (NestedSettingsProxy)
667
+ if not isinstance(dialog_settings, dict) and not hasattr(dialog_settings, 'get'):
668
668
  self.logger.error(f"Invalid dialog_settings structure: {type(dialog_settings)}, skipping refresh")
669
669
  return
670
670
 
@@ -9,6 +9,7 @@ from tkinter import scrolledtext
9
9
  import platform
10
10
  import time
11
11
  import threading
12
+ import hashlib
12
13
  from typing import Dict, List, Tuple, Optional, Any
13
14
  from dataclasses import dataclass
14
15
 
@@ -31,7 +32,7 @@ class EfficientLineNumbers(tk.Frame):
31
32
 
32
33
  # Configuration
33
34
  self.line_number_width = 50 # Adjustable width
34
- self.debounce_delay = 5 # ms - very responsive updates
35
+ self.debounce_delay = 50 # ms - balanced responsiveness vs. efficiency
35
36
  self.cache_size_limit = 1000 # Maximum cached line positions
36
37
 
37
38
  # Create widgets
@@ -280,12 +281,12 @@ class EfficientLineNumbers(tk.Frame):
280
281
  return None
281
282
 
282
283
  def _get_content_hash(self) -> str:
283
- """Get a hash of the current content for change detection."""
284
+ """Get a hash of the current content for change detection using MD5."""
284
285
  try:
285
286
  content = self.text.get("1.0", "end-1c")
286
- # Simple hash based on content length and first/last chars
287
287
  if content:
288
- return f"{len(content)}_{content[:10]}_{content[-10:]}"
288
+ # Use MD5 for reliable change detection (truncated for efficiency)
289
+ return hashlib.md5(content.encode('utf-8', errors='replace')).hexdigest()[:16]
289
290
  return "empty"
290
291
  except Exception:
291
292
  return "error"